package com.zebra.base.auto;

import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zebra.base.anno.GroupBy;
import com.zebra.base.anno.OrderBy;
import com.zebra.base.anno.SimpleQuery;
import com.zebra.base.anno.type.*;
import com.zebra.base.po.BaseEntity;
import com.zebra.base.vo.BaseForm;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;

/**
 * @ClassName SimpleQueryWrapperBuilder
 * @Description TODO 简单对象封装查询条件构造器（单表封装）。
 * @Author 消魂钉
 * @Date 6/2 0002 18:13
 */
public class SimpleQueryWrapperBuilder {
    /**
     * 排序字段
     */
    private final static String PRIORITY_FIELD = "priority";

    private static SimpleQueryWrapperBuilder instance = null;

    private QueryWrapper<BaseForm> queryWrapper = new QueryWrapper<>();

    private BaseForm<? extends com.zebra.base.po.BaseEntity> baseForm;

    private SimpleQueryWrapperBuilder() {

    }

    /**
     * SimpleQueryWrapperBuilder
     *
     * @return
     */
    public static SimpleQueryWrapperBuilder getInstance() {
        if (instance == null) {
            instance = new SimpleQueryWrapperBuilder();
        }
        return instance;
    }

    /**
     * 只处理增加注解的
     *
     * @param baseForm
     * @return
     */
    public QueryWrapper builder(BaseForm<? extends BaseEntity> baseForm) {
        queryWrapper = new QueryWrapper<BaseForm>();

        this.baseForm = baseForm;
        Field[] fields = ReflectUtil.getFields(baseForm.getClass());
//        addSelect(aonnFileds, SimpleQuery.class);
        addWhere(sortByFields(fields, SimpleQuery.class), SimpleQuery.class);
        addGroupBy(sortByFields(fields, GroupBy.class), GroupBy.class);
        addOrderBy(sortByFields(fields, OrderBy.class), OrderBy.class);
        return queryWrapper;
    }

    /**
     * 根据注解中的参数进行排序[sortByFields]
     *
     * @param fields
     * @param annotationClass
     * @return
     */
    private List<AnnoFiled> sortByFields(Field[] fields, Class<? extends Annotation> annotationClass) {
        List<AnnoFiled> fieldList = new ArrayList<>();
        if (fields != null) {
            for (Field field : fields) {
                Integer priority = AnnotationUtil.getAnnotationValue(field, annotationClass, PRIORITY_FIELD);
                if (priority != null) {
                    fieldList.add(new AnnoFiled(priority, field));
                }
            }
        }
        CollectionUtil.sort(fieldList, new Comparator<AnnoFiled>() {
            @Override
            public int compare(AnnoFiled o1, AnnoFiled o2) {
                return o1.getNum().compareTo(o2.getNum());
            }
        });
        return fieldList;
    }

    /**
     * 构造OrderBy
     *
     * @param aonnFileds
     * @param orderByClass
     */
    private void addOrderBy(List<AnnoFiled> aonnFileds, Class<OrderBy> orderByClass) {
        if (CollectionUtil.isNotEmpty(aonnFileds)) {
            aonnFileds.forEach(annoFiled -> {
                OrderBy orderBy = AnnotationUtil.getAnnotation(annoFiled.getField(), orderByClass);
                if (orderBy != null) {
                    String column = obtainColumn(orderByClass, annoFiled.getField());
                    SortType sort = orderBy.sort();
                    switch (sort) {
                        case ASC:
                            queryWrapper.orderByAsc(column);
                            break;
                        case DESC:
                            queryWrapper.orderByDesc(column);
                            break;
                        case NULL:
                            queryWrapper.orderByAsc(column);
                            break;
                    }
                }
            });
        }
    }

    /**
     * GroupBy处理器
     *
     * @param aonnFileds
     * @param groupByClass
     */
    private void addGroupBy(List<AnnoFiled> aonnFileds, Class<GroupBy> groupByClass) {
        if (CollectionUtil.isNotEmpty(aonnFileds)) {
            aonnFileds.forEach(annoFiled -> {
                GroupBy groupBy = AnnotationUtil.getAnnotation(annoFiled.getField(), groupByClass);
                if (groupBy != null) {
                    String column = obtainColumn(groupByClass, annoFiled.getField());
                    queryWrapper.select(column);//必须要一致
                    queryWrapper.groupBy(column);//必须要一致
                }
            });
        }
    }

    /**
     * 查询条件设置
     *
     * @param aonnFileds
     * @param simpleQueryClass
     */
    private void addWhere(List<AnnoFiled> aonnFileds, Class<SimpleQuery> simpleQueryClass) {
        if (CollectionUtil.isNotEmpty(aonnFileds)) {
            int size = aonnFileds.size();
            int i = 0;
            while (i < size) {
                SimpleQuery simpleQuery = AnnotationUtil.getAnnotation(aonnFileds.get(i).getField(), simpleQueryClass);
                String column = obtainColumn(SimpleQuery.class, aonnFileds.get(i).getField());
                FuncType formula = simpleQuery.formula();
                Object value = getFieldValue(aonnFileds.get(i).getField());
                if (RangeType.START == simpleQuery.range()) {
                    List<AnnoFiled> orLists = new ArrayList<>();
                    i = orAndParser(orLists, i, aonnFileds, size);
                    if (CollectionUtil.isNotEmpty(orLists)) {
                        queryWrapper.and(t -> orLists.forEach(f -> {
                            SimpleQuery sq = f.getSimpleQuery();
                            RelationStrategy rs = sq.relation();
                            switch (rs) {
                                case AND:
                                    t.and(f.getConsumer());
                                    break;
                                case OR:
                                    t.or(f.getConsumer());
                                    break;
                            }
                        }));
                    }
                } else {
                    RelationStrategy rs = simpleQuery.relation();
                    switch (rs) {
                        case AND:
                            queryWrapper.and(makeWrapperFunc(formula, column, value,true));
                            break;
                        case OR:
                            queryWrapper.or(makeWrapperFunc(formula, column, value,true));
                            break;
                    }
                }
                i++;//进入下一个判断
            }
        }
    }

    /**
     * 目前只做一层递归调用。
     *
     * @param orList
     * @param i
     * @param aonnFileds
     * @param size
     */
    private Integer orAndParser(List<AnnoFiled> orList, int i, List<AnnoFiled> aonnFileds, int size) {
        //传入的i不能超过list.size
        if (i < size) {
            AnnoFiled aonnFiled = aonnFileds.get(i);
            //向后遍历是否还有OR类型的参数 如果有加进来，一旦碰到AND则停止遍历
            SimpleQuery simpleQuery = AnnotationUtil.getAnnotation(aonnFileds.get(i).getField(), SimpleQuery.class);
            aonnFiled.setSimpleQuery(simpleQuery);
            Object value = getFieldValue(aonnFileds.get(i).getField());
            RangeType range = simpleQuery.range();
            if (ObjectUtil.isNotNull(value)) {
                String orCol = obtainColumn(SimpleQuery.class, aonnFiled.getField());
                FuncType orFor = simpleQuery.formula();
                Boolean flag = true;
                if (StrUtil.isBlank(value+"")){
                    orCol = "1";
                    value = "1";
                    flag = false;
                }
                aonnFiled.setConsumer(makeWrapperFunc(orFor, orCol, value,flag));
                orList.add(aonnFiled);
                if (RangeType.END == range) {
                    return i;
                }
                orAndParser(orList, ++i, aonnFileds, size);
            }


        }
        return i;
    }
    public Consumer<QueryWrapper<BaseForm>> makeWrapperFunc(FuncType formula, String column, Object value, Boolean flag) {
        switch (formula) {
            case EQ:
                return flag ? t -> t.eq(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case NE:
                return flag ? t -> t.ne(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case GT:
                return flag ? t -> t.gt(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case GE:
                return flag ? t -> t.ge(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case LT:
                return flag ? t -> t.lt(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case LE:
                return flag ? t -> t.le(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case BW:
                break;
            case NBW:
                break;
            case LK:
                return flag ? t -> t.like(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case NLK:
                return flag ? t -> t.notLike(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case LL:
                return flag ? t -> t.likeLeft(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case LR:
                return flag ? t -> t.likeRight(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
            case NU:
                return flag ? t -> t.isNull(ObjectUtil.isNotEmpty(value), column) : t -> t.eq(column, value);
            case NN:
                return flag ? t -> t.isNotNull(ObjectUtil.isNotEmpty(value), column) : t -> t.eq(column, value);
            case IN:
                if (value instanceof Collection) {
                    return flag ? t -> t.in(ObjectUtil.isNotEmpty(value), column, (Collection) value) : t -> t.eq(column, value);
                }
                ;
            case NI:
                if (value instanceof Collection) {
                    return flag ? t -> t.notIn(ObjectUtil.isNotEmpty(value), column, (Collection) value) : t -> t.eq(column, value);
                }
        }
        return null;
    }


//
//    public Consumer<QueryWrapper<BaseForm>> makeWrapperFunc(FuncType formula, String column, Object value, Boolean flag) {
//        switch (formula) {
//            case EQ:
//                return flag ? t -> t.eq(ObjectUtil.isNotEmpty(value), column, value) : t -> t.eq(column, value);
//            case NE:
//                return flag ? t -> t.ne(ObjectUtil.isNotEmpty(value), column, value) : t -> t.ne(column, value);
//            case GT:
//                return flag ? t -> t.gt(ObjectUtil.isNotEmpty(value), column, value) : t -> t.gt(column, value);
//            case GE:
//                return flag ? t -> t.ge(ObjectUtil.isNotEmpty(value), column, value) : t -> t.ge(column, value);
//            case LT:
//                return flag ? t -> t.lt(ObjectUtil.isNotEmpty(value), column, value) : t -> t.lt(column, value);
//            case LE:
//                return flag ? t -> t.le(ObjectUtil.isNotEmpty(value), column, value) : t -> t.le(column, value);
//            case BW:
//                break;
//            case NBW:
//                break;
//            case LK:
//                return flag ? t -> t.like(ObjectUtil.isNotEmpty(value), column, value) : t -> t.like(column, value);
//            case NLK:
//                return flag ? t -> t.notLike(ObjectUtil.isNotEmpty(value), column, value) : t -> t.notLike(column, value);
//            case LL:
//                return flag ? t -> t.likeLeft(ObjectUtil.isNotEmpty(value), column, value) : t -> t.likeLeft(column, value);
//            case LR:
//                return flag ? t -> t.likeRight(ObjectUtil.isNotEmpty(value), column, value) : t -> t.likeRight(column, value);
//            case NU:
//                return flag ? t -> t.isNull(ObjectUtil.isNotEmpty(value), column) : t -> t.isNull(column);
//            case NN:
//                return flag ? t -> t.isNotNull(ObjectUtil.isNotEmpty(value), column) : t -> t.isNotNull(column);
//            case IN:
//                if (value instanceof Collection) {
//                    return flag ? t -> t.in(ObjectUtil.isNotEmpty(value), column, (Collection) value) : t -> t.in(column, (Collection) value);
//                }
//                ;
//            case NI:
//                if (value instanceof Collection) {
//                    return flag ? t -> t.notIn(ObjectUtil.isNotEmpty(value), column, (Collection) value) : t -> t.notIn(column, (Collection) value);
//                }
//        }
//        return null;
//    }

    /**
     * @param field
     * @return
     */

    private Object getFieldValue(Field field) {
        return ReflectUtil.getFieldValue(baseForm, field);
    }

    /**
     * 列表设置
     *
     * @param field
     * @param simpleQueryClass
     */
    private void addSelect(Field field, Class<SimpleQuery> simpleQueryClass) {
        SimpleQuery simpleQuery = field.getAnnotation(simpleQueryClass);
        boolean isSelect = simpleQuery.isSelect();
        if (isSelect) {
            String column = obtainColumn(SimpleQuery.class, field);
            queryWrapper.select(column);
        }
    }

    /**
     * 获取列属性  如果 注解没有给出属性 则获取字段名称作为数据库字段
     *
     * @param field
     * @return
     */
    private String obtainColumn(Class anno, Field field) {

        String name = (String) AnnotationUtil.getAnnotationValue(field, anno, "name");
        return StrUtil.isNotBlank(name) ? name : augmentFieldNamingStrategy(anno, field);
    }

    /**
     * 根据字段命名规则进行转化
     * @return
     */
    private String augmentFieldNamingStrategy(Class anno, Field field) {
        //RelationStrategy.AND 通过反射找到对应的策略
        FieldNamingStrategy relation = (FieldNamingStrategy) AnnotationUtil.getAnnotationValue(field, anno, "strategy");
        if (relation == null) {
            return "";
        }
        switch (relation) {
            case CAPITAL_UNDERLINE_MODE:
                return StrUtil.toUnderlineCase(field.getName()).toUpperCase();
            case LOWERCASE_UNDERLINE_MODE:
                return StrUtil.toUnderlineCase(field.getName());
            default:
                return null;
        }
    }

    @Data
    @NoArgsConstructor
    private class AnnoFiled {
        //元注解方式
        private Integer num;
        private Field field;
        private Consumer<QueryWrapper<BaseForm>> consumer;
        private SimpleQuery simpleQuery;

        public AnnoFiled(Integer num, Field field) {
            this.num = num;
            this.field = field;
        }
    }
}
